OS: Windows 10
Editor: Visual Studio Code
Rust version: 1.63.0
當程式發生致命錯誤的時候,通常最直接的做法是會把錯誤訊息輸出出來,然後緊急關閉程式,一樣的,Rust也有這樣的功能:
panic!("??");
執行後會印出以下訊息:
thread 'main' panicked at '??', src\main.rs:5:5
error: process didn't exit successfully: `target\debug\basic.exe` (exit code: 101)
這樣的錯誤訊息,之前也在別的篇章見過,但沒注意到的是下面還有一段訊息,可以把一步步地把錯誤往回推:
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
在教學文件中,可以遍執行的時候,多下這行指令把錯誤往回推:
RUST_BACKTRACE=1 cargo run
但這貌似只在Unix跟macOS才有效,稍微改一下,把這指令設定在main()裡面:
use std::env;
fn main() {
    env::set_var("RUST_BACKTRACE", "1");
    panic!("??");
}
Result與可復原錯誤Result之前也有見過,在找main()是否可以回傳錯誤的時候有登場。
這個功能對比到其他語言的話就是try...catch...。
他的型別是Result<T, E>,類似Option<T>,但比起空值,他給的是錯誤,以下是例子:
use std::fs::File;
// ...
let f = File::open("hello.txt");
let f = match f {
    Ok(file) => file,
    Err(error) => panic!("Failed to open file: {:?}", error),
}
{:?}這個format是debug的format,是要讓結構去實作std::fmt::Debug這個介面,才可以讓結構直接輸出成特定格式。
再來,如果給出錯誤,我們可以對於一些特定的錯去篩選跟處理,例如:
use std::fs::File;
use std::io::ErrorKind;
// ...
// 沒有檔案的話幫忙建一個
let f = File::open("hello.txt");
let f = match f {
    Ok(file) => file,
    Err(error) => {
        if error.kind() == ErrorKind::NotFound {
            match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("{:?}", e),
            }
        } else {
            panic!("{:?}", error);
        }
    }
};
上面的處理雖然詳細,但冗長,我們可以用unwrap()跟expect()來簡化它。
// 由系統幫忙輸出panic內容
let f = File::open("hello.txt").unwrap();
// 指定輸出apnic的內容
let f = File::open("hello.txt").expect("File to open \"hello.txt\".");
use std::fs::File;
use std::io::{self, Read};
fn read_file_to_string(filename: &str) -> Result<String, io::Error> {
    let f = File::open(filename);
    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };
    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}
fn main() {
    let greeting = read_file_to_string("hello.txt");
    if let Ok(content) = greeting {
        println!("{}", content);
    } else if let Err(e) = greeting {
        panic!("{:?}", e);
    }
}
試試看以上個這個範例,如果有hello.txt的話,會輸出文件的內容,沒有的話則會丟出panic。
然後我們可以用**?** 運算子簡化read_file_to_string,這個運算子如果是Ok的話,則會以表達式(expression)回傳;如果是Err的話,則會連帶return,把整個函式回傳回去:
fn read_file_to_string(filename: &str) -> Result<String, io::Error> {
    let mut f = File::open(filename)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}
如果要再簡化,可以這樣寫:
fn read_file_to_string(filename: &str) -> Result<String, io::Error> {
    let mut s = String::new();
    File::open(filename)?.read_to_string(&mut s)?;
    Ok(s)
}